using System;
using System.Linq;
using System.Threading;
using CSScriptLibrary;
using System.Reflection;

public interface ICustomer
{
    string Name { get; }
}
            
class EvalTests
{
    static public void Test()
    {
        {
            CSScript.EvalNamespaces = "System;System.Reflection;System.Diagnostics";
            
            var ttt = new
                        {
                            Name = "name",
                            Timestamp = Environment.TickCount
                        }; 
                


            var ttttt = ttt.AlignToInterface<ICustomer>();

            var result = CSScript.Eval(
                                1, 2,
                              @"func(int a, int b){
                                    Trace.WriteLine(""'func' is called"");
 	                                return a + b;
 	                            }");

            //the next code cannot be executed as anonymous types are not sterilisable and cannot be passed to another AppDomains
//            var NewCustomer1 = CSScript.Eval("Barton",
//                                                @"func (string name) {
//                                                           return new 
//                                                                  { 
//                                                                      Name=name, 
//                                                                      Timestamp = Environment.TickCount
//                                                                  };  
//                                                       }");

            var NewCustomer = CSScript.BuildEval(@"func (string name) {
                                                           return new 
                                                                  { 
                                                                      Name=name, 
                                                                      Timestamp = Environment.TickCount
                                                                  };  
                                                       }");

            var customer111 = NewCustomer("Smith").AlignToInterface<ICustomer>();

            var customer = NewCustomer("Smith");
            dynamic customer1 = NewCustomer("Smith");


            //anonymous classes cannot be aligned -object caster needs fixing

            //ICustomer c = customer.AlignToInterface<ICustomer>();
            
            dynamic c = customer;

            dynamic c1 = new
            {
                Name = "tyreyrt",
                Timestamp = Environment.TickCount
            };

            var name = customer.GetType().GetProperty("Name").GetValue(customer, new object[0]);
            //name = customer1.Name;
            //name = c.Name;


            var Trace = CSScript.BuildEval(@"trace (object message) 
                            {
                                Trace.WriteLine(""EVAL:"" + message);  
                            }");


            var Average = CSScript.BuildEval("avrg (int a, int b)  { return (a+b)/2.0; }");

            Trace(Average(7, 8));
        }
        {
            var Trace = CSScript.BuildEval(@"trace (string message) 
                                      { 
                                          Trace.WriteLine(""EVAL:""+message);  
                                      }");

            Trace("test");

            var sum = CSScript.BuildEval(@"sum (int a, int b) 
                                      { 
                                          return  a+b; 
                                      }");

            var test = sum(1, 7);
        }
    }


    static public void CustomEvalTest()
    {
        Console.WriteLine(Eval(1, 2,
                             @"func(int a, int b){
                                  Console.WriteLine(""'func' is called"");
 	                              return a + b;
 	                          }"));
    }

    static object Eval(params object[] args)
    {
        if (args.Length == 0)
            throw new Exception("You did not specify the code to 'Eval'");
        
        object lastArg = args.Last() ;

        if (lastArg == null || !(lastArg is string))
            throw new Exception("You did not specify the code to 'Eval'");

        string code = (string)lastArg;

        string methodName = code.Split(new char[]{'(', ' '}, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();

        if (methodName == null)
            throw new Exception("The code to 'Eval' is not valid. The expected code patterns is as 'func(type arg1,..., type argN){ return <result>; }'");

        object[] newArgs = new object[args.Length - 1];

        Array.Copy(args, newArgs, newArgs.Length);

        var codeFull = @"using System;
                         public class EvalScript
                         {
                             public static object " + code + @"
                         }";

        using (var helper = new AsmHelper(CSScript.CompileCode(codeFull), null, true))
        {
            return helper.Invoke("*." + methodName, newArgs);
        }
    }
}

class ScritedObject
{
    private String textEmitter;
    private int myOrdinal;
    private Object compileLock;
    private bool uniqueMethodName;

    public void EmitTestText()
    {
        String methodNameSuffix = String.Empty;
        if (uniqueMethodName)
            methodNameSuffix = myOrdinal.ToString();

        CSScript.GlobalSettings.OptimisticConcurrencyModel = false;

        if (compileLock != null)
            Monitor.Enter(compileLock);

        AsmHelper executor = new AsmHelper(CSScript.LoadMethod("public static void Emit" + methodNameSuffix + "(int index) {" + this.textEmitter + "}"));
        if (compileLock != null)
            Monitor.Exit(compileLock);

        executor.Invoke("*.Emit" + methodNameSuffix, this.myOrdinal);
    }

    public ScritedObject(int ObjectCount, bool UniqueMethodName, Object CompileLock)
    {
        this.textEmitter = "Console.WriteLine(\"I am emitter # \" + index);";
        this.myOrdinal = ObjectCount;
        this.uniqueMethodName = UniqueMethodName;
        this.compileLock = CompileLock;
    }
}

class Concurrency
{
    public static void Test()
    {
        CSScript.CacheEnabled = true;
        //CSScript.GlobalSettings.InMemoryAsssembly = true;
        CSScript.GlobalSettings.TargetFramework = "v4.0";
        CSScript.ShareHostRefAssemblies = true;

        Object compileLock = new Object();
        //no compile sync
        compileLock = null;
        bool UniqueMethodNames = false;
        //enforce unque method names
        //UniqueMethodNames = true;

        Thread emitThread1 = new Thread
        (
            delegate()
            {
                for (int i = 0; i < 10; i++)
                {
                    ScritedObject obj = new ScritedObject(i, UniqueMethodNames, compileLock);
                    obj.EmitTestText();
                }
            }
        );

        Thread emitThread2 = new Thread
        (
            delegate()
            {
                for (int i = 10; i < 30; i++)
                {
                    ScritedObject obj = new ScritedObject(i, UniqueMethodNames, compileLock);
                    obj.EmitTestText();
                }
            }
        );

        Thread emitThread3 = new Thread
        (
            delegate()
            {
                for (int i = 20; i < 40; i++)
                {
                    ScritedObject obj = new ScritedObject(i, UniqueMethodNames, compileLock);
                    obj.EmitTestText();
                }
            }
        );

        emitThread1.Start();
        emitThread2.Start();
        emitThread3.Start();

        emitThread1.Join();
        emitThread2.Join();
        emitThread3.Join();

        Console.WriteLine("Done, any key to exit...");
        Console.ReadKey(false);
    }
}